﻿using System.Collections.Generic;
using UnityEngine;

namespace UFrame
{
    public class NavData
    {
    }

    public class NavGraph
    {
        public int id;
        public GameObject node;
        public Vector3 startPoint;
        public Vector3 endPoint;
        public Vector3 startHeightPoint;
        public Vector3 endHeightPoint;
        public float height;
        public bool visited;
        public List<JumpPointGraph> BorderUponPointList = new List<JumpPointGraph>();


        public JumpPointGraph IsInJumpPoint(Vector3 v, float distance = 1.5f, int jumpId = 0)
        {
            foreach (var item in BorderUponPointList)
            {
                if (item.id == jumpId)
                {
                    if (Vector3.Distance(item.startPosition, v) <= distance)
                    {
                        return item;
                    }
                }
            }

            return null;
        }


        public JumpPointGraph IsJumpTo(NavGraph v)
        {
            foreach (var item in BorderUponPointList)
            {
                if (item.target.id == v.id)
                {
                    return item;
                }
            }

            return null;
        }

        public bool IsInRect(Vector3 v)
        {
            return (startPoint.x < v.x && endPoint.x > v.x) && (startHeightPoint.y < v.y && endHeightPoint.y > v.y);
        }
    }

    public class JumpPointGraph
    {
        public int id;
        public Vector3 startPosition;
        public Vector3 endPosition;
        public NavGraph target;
        public NavGraph current;
    }

    /// <summary>
    /// 告诉AI如何移动过去
    /// </summary>
    public class PathAction
    {
        public int jumpId = 0;
        public Vector3 position;
        public EPathAction action = EPathAction.None;
        public NavGraph curNavGraph;
        public NavGraph targetNavGraph;
        public float distance;
    }

    public class PathActionQueue
    {
        public EPathAction action = EPathAction.None;
        public Queue<PathAction> pathQueue = new Queue<PathAction>();
    }

    public enum EPathAction
    {
        None,
        Jump,
        Move
    }


    /// <summary>
    /// 负责提供路径搜索算法
    /// </summary>
    public sealed class NavGraphMa : Singleton<NavGraphMa>
    {
        private NavGraphMa()
        {
        }

        public List<NavGraph> graphs = new List<NavGraph>();
        public Vector3 targetPoint = new Vector3();

        private List<NavGraph> path = new List<NavGraph>();
        private List<bool> visited = new List<bool>();

        private int jumpId = 1;

        public int JumpId
        {
            get { return jumpId++; }
        }

        public NavGraph LocationPoint2D(Vector3 v)
        {
            foreach (var item in graphs)
            {
                if (item.IsInRect(v))
                {
                    return item;
                }
            }

            return null;
        }

        public JumpPointGraph IsLocationJumpPoint2D(Vector3 v)
        {
            var navGraph = LocationPoint2D(v);
            if (navGraph == null)
            {
                return null;
            }

            return navGraph.IsInJumpPoint(v);
        }


        public PathActionQueue FindPath(Vector3 position, Vector3 target)
        {
            var curPoint = LocationPoint2D(position);
            var tarPoint = LocationPoint2D(target);

            PathActionQueue a = new PathActionQueue();
            a.action = EPathAction.None;

            if (curPoint == null)
            {
                //Debug.LogError("当前点没有在寻路范围内: " + position);
                return a;
            }

            if (tarPoint == null)
            {
                //Debug.LogError("目标点没有在寻路范围内: " + tarPoint);
                return a;
            }

            FindGraph(curPoint, tarPoint);

            a = ConvertNavGraphList(path, position, target);
            //return path[0].endPoint;
            return a;
        }

        public void FindGraph(NavGraph start, NavGraph end)
        {
            if (start.id == end.id)
            {
                Debug.Log("就在当前平台上:" + start.id);
                path.Clear();
                path.Add(start);
                return;
            }

            for (int i = 0; i < visited.Count; i++)
            {
                visited[i] = false;
            }

            foreach (var item in graphs)
            {
                item.visited = false;
            }

            path.Clear();
            path.Add(start);
            Find(start, end);

            #region TODO t 测试

            string pathStr = string.Empty;
            foreach (var item in path)
            {
                pathStr += " " + item.id;
            }

            Debug.Log(pathStr);

            #endregion
        }

        private bool Find(NavGraph start, NavGraph end)
        {
            start.visited = true;
            foreach (var i in start.BorderUponPointList)
            {
                NavGraph buGraph = i.target;
                if (buGraph.visited)
                {
                    continue;
                }

                buGraph.visited = true;
                if (buGraph.id == end.id)
                {
                    path.Add(end);
                    return true;
                }
                else
                {
                    path.Add(buGraph);
                    bool result = Find(buGraph, end);
                    if (result)
                    {
                        return true;
                    }
                    else
                    {
                        path.Remove(buGraph);
                    }
                }
            }

            return false;
        }


        public void LoadScenePath()
        {
            graphs.Clear();
            var list = GameObject.FindObjectsOfType<NavRect>();

            int id = 0;
            foreach (var item in list)
            {
                var navGraph = ConvertNavGraph(item);
                navGraph.id = id++;
                item.id = navGraph.id;
                graphs.Add(navGraph);
            }

            //初始化跳跃点
            foreach (var item in graphs)
            {
                foreach (var jumpGraph in item.BorderUponPointList)
                {
                    jumpGraph.target = LocationPoint2D(jumpGraph.endPosition);

                    AddReverseJumpPoint(jumpGraph.target, jumpGraph);
                }
            }
        }

        public NavGraph GetGraphById(int id)
        {
            foreach (var item in graphs)
            {
                if (item.id == id)
                {
                    return item;
                }
            }

            return null;
        }

        /// <summary>
        /// 给跳跃的目标点反向初始化点
        /// </summary>
        private void AddReverseJumpPoint(NavGraph graph, JumpPointGraph jumpGraph)
        {
            JumpPointGraph g = new JumpPointGraph();
            g.startPosition = jumpGraph.endPosition;
            g.endPosition = jumpGraph.startPosition;
            g.target = jumpGraph.current;
            g.current = graph;
            g.id = JumpId;
            graph.BorderUponPointList.Add(g);
        }


        private NavGraph ConvertNavGraph(NavRect item)
        {
            NavGraph navGraph = new NavGraph();
            navGraph.height = 5;
            navGraph.node = item.gameObject;
            navGraph.startPoint = item.StartPosition;
            navGraph.endPoint = item.EndPosition;
            navGraph.startHeightPoint = item.StartHeightPosition;
            navGraph.endHeightPoint = item.EndHeightPosition;

            foreach (JumpPoint jumpPoint in item.BorderUponPoint)
            {
                JumpPointGraph g = new JumpPointGraph();
                g.id = JumpId;
                g.startPosition = jumpPoint.startPosition;
                g.endPosition = jumpPoint.endPosition;
                g.current = navGraph;
                navGraph.BorderUponPointList.Add(g);
            }

            return navGraph;
        }

        private PathActionQueue ConvertNavGraphList(List<NavGraph> list, Vector3 curPos, Vector3 targetPos)
        {
            PathActionQueue q = new PathActionQueue();

            if (list.Count == 1)
            {
                PathAction pa = new PathAction();
                pa.action = EPathAction.Move;
                pa.position = targetPos;
                pa.curNavGraph = list[0];
                q.pathQueue.Enqueue(pa);
            }
            else
            {
                NavGraph a = null;
                NavGraph b = null;

                for (int i = 0; i < list.Count; i++)
                {
                    PathAction pa = new PathAction();

                    if (i + 1 >= list.Count) //最后一个的时候移动是移动
                    {
                        pa.action = EPathAction.Move;
                        pa.position = targetPos;
                        pa.curNavGraph = list[i];
                        q.pathQueue.Enqueue(pa);
                        break;
                    }

                    a = list[i];
                    b = list[i + 1];
                    pa.curNavGraph = a;
                    pa.position = targetPos;

                    bool shortest = true;
                    PathAction shortestPoint = null;
                    foreach (var jumpPoint in a.BorderUponPointList)
                    {
                        if (jumpPoint.target.id == b.id)
                        {
                            if (shortestPoint == null ||
                                shortestPoint.distance > Vector3.Distance(curPos, jumpPoint.startPosition))
                            {
                                if (jumpPoint.startPosition.y > jumpPoint.endPosition.y) //从上面到下面
                                {
                                    pa.action = EPathAction.Move;
                                    pa.position = jumpPoint.endPosition;
                                    pa.jumpId = jumpPoint.id;
                                    pa.distance = Vector3.Distance(curPos, jumpPoint.startPosition);
                                    shortestPoint = pa;
                                }
                                else
                                {
                                    pa.action = EPathAction.Jump;
                                    pa.position = jumpPoint.startPosition;
                                    pa.jumpId = jumpPoint.id;
                                    pa.distance = Vector3.Distance(curPos, jumpPoint.startPosition);
                                    shortestPoint = pa;
                                }
                            }

                            if (shortest == false)
                            {
                                break;
                            }
                        }
                    }


                    q.pathQueue.Enqueue(pa);
                }
            }

            return q;
        }
    }
}